#include "som.h"
/*
Une première version d'implémentation du SOM
Roxane Levy
 */



/* La fonction main() est appelée lorsque le programme C est lancé seul. Elle attend un argument : le mot de passe pour se connecter au compte MySQL. */
int main (int k , char **argv) {
    if (k < 2) {   // données et RN sont obtenus aléatoirement
        startprog(150,4,8,6,30,0,0,1) ;    // passer les arguments en ligne de commande ?
        anotherRoutine() ;
        destroyAll() ;
        return 0 ;
    }

    else {   // l'argument est alors le mot de passe MySQL
        startprog(150,4,8,6,30,0,0,1) ;    // passer les arguments en ligne de commande ?
        mainRoutine(argv[1]) ;
	    destroyAll() ;
        return 0 ;
    }
}

/* La fonction startprog() récupère la valeur des paramètres du réseau */
int startprog(int a,int b, int c, int d, int e, int f, int g, int h) {
    N_L_input_V = a;    // le nombre de vecteurs d'apprentissage
    N = b;              // la taille des vecteurs d'apprentissage
    map_x = c ;           // dimension du RN
    map_y = d ;             // dimension du RN
    nbIter = e ;            // le nombre d'itérations
    rateDecay = f ;         // l'évolution du taux d'apprentissage
    typeNeigh = g ;         // le type de voisinage
    flagshuffle = h ;       // pour charger les vecteurs dans l'ordre ou le désordre
    return 0 ;
}


void validateParameters(Data * data, Net * net, Options * options) {
    if (data->N <= 0 || data->N_L_input_V <=0) {
        fprintf(stderr, "Le nombre de vecteurs d'apprentissage et leur taille doit être supérieur à 0\n") ;
        exit(1) ;
    }

    if (net->map_x <=0 || net->map_y <=0)  {
        fprintf(stderr, "Les tailles du RN doivent être supérieures à 0\n");
        exit(1) ;
    }

    if (options->learningRateDecay != 0 && options->learningRateDecay != 1 && options->learningRateDecay != 2 )  {
        fprintf(stderr, "Le ratedecay doit être 0,1 ou 2\n");
        exit(1) ;
    }

      if (options->typeNeighbourhood != 0 && options->typeNeighbourhood!= 1 && options->typeNeighbourhood != 2 )  {
        fprintf(stderr, "Le typeNeighbourhood doit être 0,1 ou 2\n");
        exit(1) ;
    }

    if (options->flagshuffle != 0 && options->flagshuffle != 1)  {
        fprintf(stderr, "Flagcontrole doit être égal à 0 ou 1\n") ;
        exit(1) ;
    }

    }


/* La fonction mainRoutine() est la fonction qui se charge d'initialiser les données et le réseau de neurones. Elle appelle ensuite les fonctions input_random() ou input_sequence() selon l'option désirée */
int mainRoutine (char * password) {

    /* Attribution aux structures des paramètres souhaités */

    myoptions->learningRateDecay = rateDecay ;
    myoptions->typeNeighbourhood = typeNeigh ;
    myoptions->flagshuffle = flagshuffle  ;// 0 en séquence, 1 désordonné

    mydata->N_L_input_V = N_L_input_V ;
    mydata->N = N;

    mynet->neuroneSize = mydata->N ;
    mynet->map_x = map_x ;
    mynet->map_y = map_y ;


    myoptions->nb_iter =  nbIter ;


    /* Il faudrait encore quelques vérifications sur la validité des paramètres */
    validateParameters(mydata,mynet,myoptions) ;

    /* Initialisation des poids du RN ainsi que des vecteurs d'apprentissage */

    initialiseDatafromDatabase(mydata,password) ;

    initialiseRNandMore(mynet);


    if (myoptions->flagshuffle == 0) { input_sequence(mydata,mynet,myoptions); return 0 ; }
    if (myoptions->flagshuffle == 1) {input_random(mydata,mynet,myoptions) ; return 0 ; }
    return 0;
}



/* Fonction qui se connecte à la MYSQL DATABASE (il faut un mot de passe),  ne modifie que les données de la structure DATA, normalise les données */
void initialiseDatafromDatabase(Data * data, char * password) {
    //Déclaration du pointeur de structure de type MYSQL
    MYSQL mysql;
    //Initialisation de MySQL
    mysql_init(&mysql);
    //Options de connexion
    mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"option");

    //Si la connexion réussie...
    if(mysql_real_connect(&mysql,"localhost","root",password,"ADPD_Database",0,NULL,0))
    {
        //Requête qui sélectionne tout dans la table concernée
        mysql_query(&mysql, "SELECT * FROM Iris");

        //Déclaration des objets
        MYSQL_RES *result = NULL;
        MYSQL_ROW row;

        int i ;
        int col ;

        //On met le jeu de résultat dans le pointeur result
        result = mysql_store_result(&mysql);

        data->N_L_input_V = mysql_num_rows(result) ;   // la taille des vecteurs d'apprentissage est récupérée

        col  = mysql_num_fields(result);   //nombre de colonnes = nombres de champs

        data->N = col - 2 ;  // 1ère colonne est l'ID , la dernière est le champ cible, tous les deux inutiles pour l'instant

	    /* allocation de mémoire pour la matrice des vecteurs d'apprentissage */
        data->vecApprentissage = (float **)malloc(sizeof(float*) * data->N_L_input_V) ;
        for (i = 0 ; i < data->N_L_input_V ; i++) data->vecApprentissage[i] = (float * ) malloc(sizeof(float) * data->N) ;

        int j = 0 ;
        while ((row = mysql_fetch_row(result)))
        {
            //On fait une boucle pour avoir la valeur de chaque champs
            for(i = 1; i < data->N+1; i++)
            {
               //On ecrit toutes les valeurs
               data->vecApprentissage[j][i-1] =  (float)strtod(row[i],NULL) ;
            }

            j+= 1 ;
        }

        //Libération du jeu de résultat
        mysql_free_result(result);

        //Fermeture de MySQL
        mysql_close(&mysql);

    }
    else  //La connexion MYSQL a échouée
    {
        printf("Une erreur s'est produite lors de la connexion à la BDD!");
    }

    /* normalisation des données */
    normalizeData(data) ;


}


/* Fonction qui normalise les données d'apprentissage */
void normalizeData(Data * data) {
    int i,j ;
    float norme ;
    for (i = 0 ; i < data->N_L_input_V ; i++) {
        norme = normeVec(data->vecApprentissage[i],data->N) ;
        for (j = 0 ; j < data->N ; j++) data->vecApprentissage[i][j] = data->vecApprentissage[i][j] / norme ;
        }
}



/* Fonction qui retourne la norme d'un vecteur de flottants */
float normeVec(float vec[] , int vectorSize) {
    float n = 0 ;
    int i ;
    for (i= 0 ; i < vectorSize ; i++) n+= pow(vec[i],2) ;
    return sqrt(n) ;
}



/* Fonction qui s'occupe d'initialiser aléatoirement les poids des neurones, l'activation map, la matrice de voisinage. Le RN est normalisé*/
void initialiseRNandMore(Net * net) {

    /* allocation de mémoire pour le RN*/
    net->RN = (float ***)malloc(sizeof(float**) * net->map_x) ;
    int i,j ;
    for (i = 0; i < net->map_x; i++) {
        net->RN[i] = (float**)malloc(sizeof(float*)  * net->map_y) ;
            for (j= 0 ; j < net->map_y; j++) {
                net->RN[i][j] = (float *) malloc(sizeof(float) * net->neuroneSize) ;  //chaque neurone du RN est un vecteur
            }
     }

    srand (time(NULL));
    /* rempli le RN avec des poids aléatoires */
    for (i = 0; i < net->map_x; i++) {
        for (j= 0 ; j < net->map_y; j++) genereVec(net->RN[i][j], net->neuroneSize) ;
    }


    /* allocation de mémoire pour la map d'activation (elle contiendra l'état d'activation de chaque neurone) */
    net->activation_map = (float **)malloc(sizeof(float*) * net->map_x) ;

    for (i = 0; i < net->map_x; i++) {
        net->activation_map[i] = (float*)malloc(sizeof(float)  * net->map_y) ;
    }

    /* allocation de mémoire pour le voisinage */
    net->h = (float **)malloc(sizeof(float*) * net->map_x) ;
    for (i = 0; i < net->map_x; i++) {
        net->h[i] = (float*)malloc(sizeof(float)  * net->map_y) ;
    }



    /* normaliser le RN */
    normalizeRN(net) ;

 }



/* La fonction update() met à jour le réseau de neurones */
void update(Net * net, Options * options,int iteration) {
    int col, row ;
    int i,j ;
    float distance ;
    float r[net->neuroneSize] ;

    if (options->learningRateDecay == 0) {   // décroissance linéaire
       options->alpha = options->initialLearningRate - ((float)iteration/(float)options->nb_iter) ;

        }
    if (options->learningRateDecay == 1 ) {  // décroissance en inverse temps
        options->alpha = options->initC / (options->initC + iteration) ;

        }
    if (options->learningRateDecay == 2) {   // un autre type de décroissance
           options->alpha = options->initialLearningRate/ ( 1.0 + (float)iteration/(float)options->nb_iter) ;    }

   if (options->typeNeighbourhood == 0 ) {  // cette version correspond à la version simplifiée de Kohonen*/
        /* calcul du rayon de voisinage en cours */
        options->sig = options->initialSigma * (1.0 - (float)iteration/(float)options->nb_iter) ;

        for (i = 0; i < net->map_x; i++) {     // parcours de la map
            for (j= 0 ; j < net->map_y; j++) {
                col = i ;
                row = j ;
                // calcul de la distance qui sépare le bmu du neurone en cours
                distance = fabsf(net->bmuCoord[0] - col) + fabsf(net->bmuCoord[1] - row) ;
                if (distance < options->sig)   // le neurone est dans le rayon du voisinage, on met à jour
                    addArray(net->RN[col][row],substractArrayandMultiply(r, net->captr, net->RN[col][row],net->neuroneSize,options->alpha), net->neuroneSize) ;
                    // wij = wij + (x - w) * alpha }
                normalizeVector(net->RN[col][row], net->neuroneSize) ;
            }
        }
    }

    if (options->typeNeighbourhood == 1 ) {   // cette version suit la règle d'apprentissage du cours, avec un voisinage en gaussienne
        options->sig = options->initialSigma * (1.0 - (float)iteration/(float)options->nb_iter) ;

       mapNeighbourhood(net,options) ;   // h(t) = h(t) * alpha
       for (i = 0; i < net->map_x; i++) {
            for (j= 0 ; j < net->map_y; j++) {
                col = i ;
                row = j ;
                distance = fabsf(net->bmuCoord[0] - col) + fabsf(net->bmuCoord[1] - row) ;
                if (distance < options->sig) {
                    addArray(net->RN[col][row],substractArrayandMultiply(r, net->captr, net->RN[col][row],net->neuroneSize,net->h[col][row]), net->neuroneSize) ; }
                    // wij = wij + (x - w) * h
                normalizeVector(net->RN[col][row], net->neuroneSize) ;

            }
       }
    }

    if (options->typeNeighbourhood == 2 ) {   // cette version met à jour tous les neurones de la map
        options->sig = 1.0 / (1.0 + (float)iteration/(float)options->nb_iter) ;
        mapNeighbourhood(net,options) ; // h(t) = h(t) * alpha
        for (i = 0 ; i < net->map_x ; i++) {
            for (j = 0 ; j < net->map_y ; j ++) {
                col = i ;
                row = j ;
                distance = fabsf(net->bmuCoord[0] - col) + fabsf(net->bmuCoord[1] - row) ;
                addArray(net->RN[col][row],substractArrayandMultiply(r,net->captr, net->RN[col][row],net->neuroneSize,net->h[col][row]), net->neuroneSize) ;
                  // wij = wij + (x - w) * h
                normalizeVector(net->RN[col][row], net->neuroneSize) ;
            }
        }
     }
}


/* Fonction qui soustrait deux vecteurs et qui multiplie le vecteur résultant par un multiplier. */
float * substractArrayandMultiply(float * r, float array1[], float array2[], int sizeArray, float multiplier) {
    int i ;
    for (i = 0 ; i < sizeArray ; i++) {
        r[i] = array1[i] - array2[i] ;
        r[i] = r[i] * multiplier ;
        }
    return r ;
    }


/* La fonction initNeighbourhoodVariable initiale les valeurs initiales du réseau, c'est à dire le taux d'apprentissage, le rayon initial du voisinage */
void initNeighbourhoodVariable(Net * net,Options * options) {
    options->initialLearningRate = 1.0 ;   // dans tous les cas, le taux d'apprentissage commence à 1.0

    if (options->typeNeighbourhood == 0 || options->typeNeighbourhood == 1)
        options->initialSigma = maxbetweenTwoints(map_x,map_y)/ 2 ;  // le rayon initial représente 50 % de la map

    if (options->learningRateDecay == 1)  // pour un taux d'apprentissage en inverse temps il faut une constante initC
        options->initC = (float)options->nb_iter/100 ;

    if (options->typeNeighbourhood == 2)   // pour l'apprentissage qui mettra à jour tous les neurones, le rayon initial est mis à 1.0
        options->initialSigma = 1.0 ;

}


/* Fonction qui met à jour le voisinage*/
void mapNeighbourhood(Net * net,Options * options) {
        float d = 2 * M_PI * options->sig * options->sig ;
        float neigx[net->map_x] ;
        float neigy[net->map_y]  ;
        int i ;
        for (i = 0 ; i < net->map_x ; i++) neigx[i] = i ;
        for (i = 0 ; i < net->map_y ; i++) neigy[i] = i ;
        subIntToVector(neigx,net->bmuCoord[0],net->map_x) ;
        subIntToVector(neigy,net->bmuCoord[1],net->map_y) ;
        power(neigx,2,net->map_x) ;
        power(neigy,2,net->map_y) ;
        divideVecbyFloat(neigx,d,net->map_x) ;
        divideVecbyFloat(neigy,d,net->map_y) ;
        exponentialVec(neigx,net->map_x) ;
        exponentialVec(neigy,net->map_y) ;
        int j ;
        for (i = 0 ; i < net->map_x ; i++) {
            for (j = 0 ; j < net->map_y ; j++) net->h[i][j] = neigx[i] * neigy[j] * options->alpha ;
        }
}






/* Parcours itératif qui charge les vecteurs dans le désordre */
void input_random(Data * data, Net * net, Options * options) {
    initNeighbourhoodVariable(net,options) ; // on met à jour les variables initiales selon l'option désirée
    int iter ;
    int idx ;
    int shuffle_order[data->N_L_input_V] ;
    for(idx = 0; idx < data->N_L_input_V ; idx++) shuffle_order[idx] = idx ;   // les indices ordonnées
    fprintf(stderr, "Apprentissage en cours...\n") ;
    for (iter = 0 ; iter < options->nb_iter ; iter++) {   // pour le nombre d'itérations
        /* charge tous les vecteurs d'apprentissage dans le désordre */
        shuffleVec(shuffle_order,data->N_L_input_V) ;   //permute les indices
        // printf("Iteration %i\n" , iter) ;
        /* shuffle_order contient maintenant les indices réordonnés, on affiche le parcours */
        for(idx = 0; idx < data->N_L_input_V ; idx++) {
            net->captr = *(data->vecApprentissage + shuffle_order[idx]);    //positionnement sur le capteur
            findbestmatchingnode(net) ;
            update(net,options,iter+1) ;
        }
    }

    fprintf(stderr, "Terminé!\n") ;
}


/* parcours itéraif qui charge les vecteurs d'apprentissage dans l'ordre */
void input_sequence(Data * data,Net * net,Options * options) {
    initNeighbourhoodVariable(net,options) ; // on met à jour les variables initiales selon l'option désirée
    int iter ;
    int idx ;
    fprintf(stderr, "Apprentissage en cours...\n") ;
    for (iter = 0 ; iter < options->nb_iter ; iter++) {   // pour le nombre d'itérations
        /* charge tous les vecteurs d'apprentissage dans l'ordre */
        //printf("Itération numéro %i\n", iter+1);  //affichage de l'itération en cours
        for(idx = 0; idx < data->N_L_input_V ; idx++) {
            net->captr = *(data->vecApprentissage + idx);   //passe le pointeur sur les capteurs du RN
            findbestmatchingnode(net) ;
            update(net,options,iter+1) ;
        }
    }
     fprintf(stderr, "Terminé!\n") ;
}



/* Fonction qui retourne le plus grand entier des deux arguments */
int maxbetweenTwoints(int a, int b) {
    if (a >= b) return a ;
    else return b ;
    }






/* libération de la mémoire allouée au réseau de neurones */
void destroyRNandMore(Net * net) {
    int i,j ;
    for(i=0;i<net->map_x;i++){
        for (j= 0 ; j < net->map_y; j++) free(net->RN[i][j]);
        free(net->activation_map[i]) ;
        free(net->RN[i]) ;
        free(net->h[i]) ;
    }
    free(net->activation_map) ;
    free(net->RN);
    free(net->h) ;
}


/* Générateur de vecteur de flottants */
void genereVec(float * vec, int size) {
    int i ;
        for (i = 0 ; i < size ; i++) vec[i] = float_aleatoire_borne(0,1) ;
}




/* retourne un flottant borné entre deux valeurs max et min passées en argument */
float float_aleatoire_borne (float min, float max) {
    return rand()/ (float)RAND_MAX * (max - min) + min;
}



/* Fonction qui permet d'afficher le réseau neuronal */
void afficheRN(Net * net) {
    int i,j ;
    for (i = 0; i < net->map_x; i++) {
        for (j= 0 ; j < net->map_y; j++) affiche_vecteur(net->RN[i][j], net->neuroneSize) ;
        printf("\n") ;
    }
}



/* fonction qui permet d'afficher un vecteur de float*/
void affiche_vecteur(float vector[], int tailleVecteur) {
    int i ;
    for (i = 0 ; i < tailleVecteur ; i++) printf("%.12f ",vector[i]);
    printf("\n") ;
}

/* fonction qui permet d'afficher un vecteur de float*/
void affiche_vecteurint(int vector[], int tailleVecteur) {
    int i ;
    for (i = 0 ; i < tailleVecteur ; i++) printf("%i ",vector[i]);
    printf("\n") ;
}


/* fonction qui permet d'afficher la matrice contenant les vecteurs d'apprentissage */
void afficheData(Data * data) {
    int i ;
    for (i= 0 ; i < data->N_L_input_V;i++) affiche_vecteur(data->vecApprentissage[i], data->N) ;
}


/* Fonction qui permet d'afficher la map d'activation */
void afficheActivationMap(Net * net) {
    int i ;
    for (i= 0; i < net->map_x ; i++) affiche_vecteur(net->activation_map[i],net->map_y) ;
    printf("\n") ;
}






/* Fonction qui trouve le bmu (best matching unit). Pour cela, l'activation map va contenir les distances euclidiennes entre chaque vecteur de neurone et le vecteur de données présenté
  La fonction smallestvalue() est alors appelée pour retourner les coordonnées du neurones avec la plus petite distance euclidienne*/
void findbestmatchingnode(Net * net){
    int i,j ;
    for (i = 0; i < net->map_x; i++) {
        for (j= 0 ; j < net->map_y; j++)
            net->activation_map[i][j] = euclidian(net->RN[i][j], net->captr, net->neuroneSize);
    }
    smallestvalue(net) ;

}


/* Fonction qui cherche la plus petite valeur dans la map d'activation, et associe les coordonnées au BMU */
void smallestvalue(Net * net) {
    int c,r ;
    /* choisis des coordonnées aléatoires */
    c = int_aleatoire_borne(net->map_x) ;
    r = int_aleatoire_borne(net->map_y) ;
    float match = net->activation_map[c][r] ;
    int i,j ;
    int min_x = c ;
    int min_y = r ;
    for (i = 0 ; i < net->map_x ; i++) {
        for (j = 0 ; j < net->map_y ; j++) {
            if (net->activation_map[i][j] < match) {
                match = net->activation_map[i][j] ;
                min_x = i ;
                min_y = j ;

            }
        }
     }
     net->bmuCoord[0] = min_x ;
     net->bmuCoord[1] = min_y ;
     }



/* Fonction qui retourne la distance euclidienne entre deux vecteurs */
float euclidian(float vectorMap[], float vectorData[], int sizeVec) {
    float dist = 0 ;
    int i ;
    for (i=0; i < sizeVec;i++)
        dist += (vectorData[i] - vectorMap[i]) *  (vectorData[i] - vectorMap[i]) ;
    return sqrt(dist) ;
}




/*Fonction qui additionne deux vecteurs de float de même dimension. Le résultat est stocké dans le premier vecteur */
void addArray(float array1[], float array2[], int sizeArray) {
    int i ;
    for (i = 0 ; i < sizeArray ; i++) array1[i] += array2[i] ;
}



/* Fonction qui normalise le réseau de neurones */
void normalizeRN(Net * net) {
    int i,j,m ;
    float norme ;
    for (i = 0 ; i < net->map_x ; i++) {
        for (j = 0 ; j < net->map_y ; j++) {
            norme = normeVec(net->RN[i][j],net->neuroneSize) ;
            for (m= 0 ; m < net->neuroneSize ; m++) net->RN[i][j][m] = net->RN[i][j][m] / norme ;
        }
    }
}





/* Fonction qui permutre les indices d'un vecteur (algo du Knuth shuffle) */
void shuffleVec(int sourceVecteur[], int tailleVecteur)
{
   srand(time(NULL));

   int i;
   int index, temp;
   for (i = tailleVecteur - 1; i > 0; i--)
   {
      index = rand() % (i+1);
      temp = sourceVecteur[index];
      sourceVecteur[index] = sourceVecteur[i];
      sourceVecteur[i] = temp;
   }
}



void destroyRN(Net * net) {
    int i,j ;
    for(i=0;i<net->map_x;i++){
        for (j= 0 ; j < net->map_y; j++) {
            free(net->RN[i][j]);
        }
        free(net->RN[i]) ;
    }
    free(net->RN);
}




/* libération de la mémoire allouée à la matrice d'apprentissage */
void destroyData(Data * data) {
    int i ;
    for (i= 0 ; i < data->N_L_input_V ; i++)
        free(data->vecApprentissage[i]) ;
    free(data->vecApprentissage) ;
}



/* Fonction qui retourne un entier (positif) aléatoire borné*/
int int_aleatoire_borne(int max) {
    return rand() % max;
}


/* Fonction qui retourne un entier aléatoire */
int int_aleatoire(void) {
    return rand();
}



/* Fonction qui libère la mémoire utilisée */
int destroyAll() {

    destroyData(mydata) ;
    destroyRNandMore(mynet) ;
    return 0 ;
    }




/* Fonction qui permet de soustraire un entier s à un vecteur */
void subIntToVector(float vec[], int s, int vectorSize) {
    int i ;
    for (i = 0 ; i < vectorSize ; i++) vec[i] = vec[i] - s ;

}

/* Fonction qui permet d'élever un vecteur à la puissance s*/
void power(float vec[], int s, int vectorSize) {
    int i ;
    for (i= 0 ; i < vectorSize; i ++) vec[i] = -pow(vec[i] , 2) ;
}

/* Fonction qui divise un vecteur (cad toutes ses cases) par un flottant */
void divideVecbyFloat(float vec[], float d, int vectorSize) {
    int i ;
    for (i= 0 ; i < vectorSize ; i++) vec[i] = vec[i] / d ;
}


/* Fonction qui normalise un vecteur */
void normalizeVector(float vec[], int sizeVector) {
    int j ;
    float norme ;
    norme = normeVec(vec,sizeVector) ;
    for (j = 0 ; j < sizeVector ; j++) vec[j] = vec[j] / norme ;
        }


void exponentialVec(float vec[], int vectorSize) {
    int i ;
    for (i = 0 ; i < vectorSize ; i ++) vec[i] = exp(vec[i]) ;
}

/* Fonction qui permet d'afficher la matrice de voisinage */
void afficheNeighbourhood(Net * net) {
    int i ;
    for (i= 0 ; i < net->map_y ; i++) affiche_vecteur(net->h[i],net->map_x) ;
}



/* Fonction appelée par le script Python pour récupérer les données */
float ** getData() {
    return mydata->vecApprentissage ;
}



/* Fonction appelée par le script Python pour récupérer le réseau de neurones */
float *** getRN() {
    return mynet->RN;
}


/* Fonction appelée par le script Python pour récupérer la map d'activation */
float ** getActiMap() {
	return mynet->activation_map ;
}





/* initialise les poids du RN et les vecteurs d'apprentissage de manière aléatoire */
void initialiseRandomly(Data * data, Net * net) {
    /* allocation de mémoire pour le RN */
    net->RN = (float ***)malloc(sizeof(float**) * net->map_x) ;
    int i,j ;
    for (i = 0; i < net->map_x; i++) {
        net->RN[i] = (float**)malloc(sizeof(float*)  * net->map_y) ;
            for (j= 0 ; j < net->map_y; j++) {
                net->RN[i][j] = (float *) malloc(sizeof(float) * net->neuroneSize) ;
            }
     }

    srand (time(NULL));
    /* rempli le RN avec des poids aléatoires */
    for (i = 0; i < net->map_x; i++) {
        for (j= 0 ; j < net->map_y; j++) genereVec(net->RN[i][j], net->neuroneSize) ;
    }

    /* allocation de mémoire pour la matrice des vecteurs d'apprentissage */
    data->vecApprentissage = (float **)malloc(sizeof(float*) * data->N_L_input_V) ;
    for (i = 0 ; i < data->N_L_input_V ; i++) data->vecApprentissage[i] = (float * ) malloc(sizeof(float) * data->N) ;

    /* rempli les vecteurs d'apprentissage de manière aléatoire */
    for (i= 0 ; i < data->N_L_input_V; i++) genereVec(data->vecApprentissage[i], data->N) ;


    /* normaliser les données */
    normalizeData(data) ;

     /* allocation de mémoire pour la map d'activation (elle contiendra l'état d'activation de chaque neurone) */
    net->activation_map = (float **)malloc(sizeof(float*) * net->map_x) ;

    for (i = 0; i < net->map_x; i++) {
        net->activation_map[i] = (float*)malloc(sizeof(float)  * net->map_y) ;
    }

    /* allocation de mémoire pour le voisinage */
    net->h = (float **)malloc(sizeof(float*) * net->map_x) ;
    for (i = 0; i < net->map_x; i++) {
        net->h[i] = (float*)malloc(sizeof(float)  * net->map_y) ;
    }


    /* normaliser le RN */
    normalizeRN(net) ;

}





/* La fonction anotherRoutine() est la fonction qui se charge d'initialiser les données et le réseau de neurones. Elle appelle ensuite les fonctions input_random() ou input_sequence() selon l'option désirée */
int anotherRoutine () {

    /* Attribution aux structures des paramètres souhaités */

    myoptions->learningRateDecay = rateDecay ;
    myoptions->typeNeighbourhood = typeNeigh ;
    myoptions->flagshuffle = flagshuffle  ;// 0 en séquence, 1 désordonné

    mydata->N_L_input_V = N_L_input_V ;
    mydata->N = N;

    mynet->neuroneSize = mydata->N ;
    mynet->map_x = map_x ;
    mynet->map_y = map_y ;


    myoptions->nb_iter =  nbIter ;
    if (myoptions->flagshuffle != 0 && myoptions->flagshuffle != 1)  {
        fprintf(stderr, "Flagcontrole doit être égal à 0 ou 1\n") ;
        exit(1) ;
    }

    /* Il faudrait encore quelques vérifications sur la validité des paramètres */
     validateParameters(mydata,mynet,myoptions) ;

    /* Initialisation des poids du RN ainsi que des vecteurs d'apprentissage */

    initialiseRandomly(mydata,mynet) ;


    if (myoptions->flagshuffle == 0) input_sequence(mydata,mynet,myoptions);
    if (myoptions->flagshuffle == 1) input_random(mydata,mynet,myoptions) ;
    return 0;
}



